#include "dag_resource_analyzer.h"

#include <list>

using namespace dag;
using namespace std;

ResourceAnalyzer::ResourceAnalyzer() {
}

ResourceAnalyzer::~ResourceAnalyzer() throw() {
}

void ResourceAnalyzer::checkResources(const Graph& graph, list<GraphError>& errors) {
	
	if (graph.getHeadNode() == NULL) {
		throw MissingHeadNode();
	}
	
	if (graph.getTailNode() == NULL) {
		throw MissingTailNode();
	}
	
	m_Visited.clear();
	pathRecurseForward(graph,
					   *graph.getHeadNode(), 
					   false,
					   errors);
	
	m_Visited.clear();
	pathRecurseReverse(graph, 
					   *graph.getTailNode(), 
					   false, 
					   errors);
	m_Visited.clear();
}

bool ResourceAnalyzer::pathRecurseForward(const Graph& graph, 
										  const Node& node, 
										  bool blocked,
										  list<GraphError>& errors) {
	
	/* recurse through and make sure resources are available if the node needs
	 * it. If the path is "blocked", then the a previous node needs a resource.
	 */
	
	/* check whether this node has already been visited.  If there are loops in
	 * the graph we have to avoid recursing infinitely */
	if (m_Visited.count(&node) == 1) {
		return true;
	}
	
	m_Visited.insert(&node);
	LearnResource node_lr = node.traits().resources;
	bool noConflict = true;
	if (blocked) {
		if (node_lr == LearnResource_Inputs ||
			node_lr == LearnResource_InputsOutputs) {
			errors.push_back(MissingInputs(&node));
			noConflict = false;
		}
	}
	
	if (node_lr == LearnResource_Outputs ||
		node_lr == LearnResource_InputsOutputs) {
		/* if this node needs outputs, mark as blocked and continue path
		 * recursion */
		blocked = true;
		const vector<Edge*>& edges = graph.getOutputEdges(&node);
		for (size_t i = 0; i < edges.size(); i++) {
			if (edges[i]->to == NULL) {
				throw NullPtr();
			}
			if (!pathRecurseForward(graph, *edges[i]->to, blocked, errors)) {
				/* this node cannot get output learning resources */
				errors.push_back(MissingOutputs(&node));
				noConflict = false;
			}
		}	
	}
	else {
		/* this node doesn't require any outputs, and neither do any previous
		 * nodes. It's safe to keep recursing */
		const vector<Edge*>& edges = graph.getOutputEdges(&node);
		for (size_t i = 0; i < edges.size(); i++) {
			if (edges[i]->to == NULL) {
				throw NullPtr();
			}
			pathRecurseForward(graph, *edges[i]->to, blocked, errors);
		}
	}
	
	return noConflict;
}

bool ResourceAnalyzer::pathRecurseReverse(const Graph& graph, 
										  const Node& node, 
										  bool blocked,
										  list<GraphError>& errors) {
	/* recurse through and make sure resources are available if the node needs
	 * it. If the path is "blocked", then the a previous node needs a resource.
	 */
	
	/* check whether this node has already been visited.  If there are loops in
	 * the graph we have to avoid recursing infinitely */	
	if (m_Visited.count(&node) == 1) {
		return true;
	}
	
	m_Visited.insert(&node);
	
	LearnResource node_lr = node.traits().resources;
	bool noConflict = true;
	if (blocked) {
		if (node_lr == LearnResource_Outputs ||
			node_lr == LearnResource_InputsOutputs) {
			errors.push_back(MissingOutputs(&node));
			noConflict = false;
		}
	}
	
	if (node_lr == LearnResource_Inputs ||
		node_lr == LearnResource_InputsOutputs ||
		node.traits().canReverse == false) {
		/* if this node needs inputs, mark as "blocked" and continue path
		 * recursion */
		blocked = true;
		const vector<Edge*>& edges = graph.getInputEdges(&node);
		for (size_t i = 0; i < edges.size(); i++) {
			if (edges[i]->from == NULL) {
				throw NullPtr();
			}
			if (!pathRecurseReverse(graph, *edges[i]->from, blocked, errors)) {
				/* this node cannot get output learning resources */
				errors.push_back(MissingInputs(&node));
				noConflict = false;
			}
		}	
	}
	else {
		/* this node doesn't require any inputs, and neither do any previous
		 * nodes. It's safe to keep recursing */
		const vector<Edge*>& edges = graph.getInputEdges(&node);
		for (size_t i = 0; i < edges.size(); i++) {
			if (edges[i]->from == NULL) {
				throw NullPtr();
			}
			pathRecurseReverse(graph,
							   *edges[i]->from,
							   blocked, 
							   errors);
		}
	}
	
	return noConflict;
}
